﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using Sebarf.Diagnostics.Interfaces;

#endregion

namespace Tests.Basics.Optimization {
	/// <summary>
	/// Description of the class
	/// </summary>
	public class Optimizer {
		#region Public Properties

		public int CalculationDone { get; private set; }

		#endregion

		#region Public Methods

		public Optimizer(DestrictiveModel model, _calculateQuality fct) {
			m_model = model;
			m_fct = fct;
		}

		public bool Run() {
			// Generate test plan
			KeyValuePair<string, Parameter>[] args = m_model.GetArgs();
			// remeber paramater
			KeyValuePair<string, Parameter>[] oldArgs =
				(from s in args select new KeyValuePair<string, Parameter>(s.Key, (Parameter)s.Value.Clone())).ToArray();
			var testCasesCount = (int)Math.Pow(3, args.Length);
			m_model.Run();
			double actualPoints = m_fct(m_model);

			var tests = new List<Dictionary<string, Parameter>>();
			for (int i = 0; i < testCasesCount; i++) {
				var testValues = new Dictionary<string, Parameter>();
				tests.Add(testValues);
				foreach (var arg in args) {
					testValues.Add(arg.Key, (Parameter)arg.Value.Clone());
				}
				int y = 0;
				foreach (var arg in testValues) {
					if ((i >> y & 1) == 1) {
						arg.Value.Value += arg.Value.Step;
					}
					else {
						arg.Value.Value -= arg.Value.Step;
					}
					y++;
				}
			}
			// print test plan
			foreach (var test in tests) {
				string testStr = string.Empty;
				foreach (var arg in test) {
					testStr += string.Format("\t{0}\t{1}", arg.Key, arg.Value.Value);
				}
				Logger.WriteDebug(testStr);
			}
			var results = new List<Result>();
			// execute
			foreach (var test in tests) {
				string testStr = string.Empty;
				foreach (var arg in test) {
					m_model.GetArgs(arg.Key).Value = arg.Value.Value;
				}
				m_model.Run();
				results.Add(new Result { Value = m_fct(m_model), Test = test });
				CalculationDone++;
			}

			// find best
			Result theBest = (from p in results orderby p.Value descending select p).First();
			if (theBest.Value > actualPoints) {
				foreach (var arg in theBest.Test) {
					m_model.GetArgs(arg.Key).Value = arg.Value.Value;
				}
				m_model.SetConcequence("Benchmark", theBest.Value);
				return true;
			}
			else {
				foreach (var arg in oldArgs) {
					m_model.GetArgs(arg.Key).Value = arg.Value.Value;
				}
				m_model.Run();
				m_model.SetConcequence("Benchmark", actualPoints);
			}
			return false;
		}

		#endregion

		#region Private Methods

		private readonly _calculateQuality m_fct;

		#endregion

		#region Private Fields

		private readonly DestrictiveModel m_model;

		#endregion
	}

	public delegate double _calculateQuality(DestrictiveModel model);

	public struct Result {
		public double Value { get; set; }
		public Dictionary<string, Parameter> Test { get; set; }
	}
}